/***
 * * PROJECT
 * *    ATaG
 * * VERSION
 * *    $Revision: 1.13 $
 * * DATE
 * *    $Date: 2007/01/29 18:43:34 $
 * * LOG
 * *    $Log: NetworkTopology.java,v $
 * *    Revision 1.13  2007/01/29 18:43:34  lmottola
 * *    getNodeByID becomes getNodeInfoByID.
 * *
 * *    Revision 1.12  2007/01/29 17:48:01  lmottola
 * *    Added debug info in toString() method.
 * *
 * *    Revision 1.11  2007/01/28 21:57:24  lmottola
 * *    Fix bug in getNodesWithAttribute.
 * *
 * *    Revision 1.10  2007/01/05 09:31:46  lmottola
 * *    Modified constructor in NetworkTopology.
 * *
 * *    Revision 1.9  2007/01/04 09:11:22  lmottola
 * *    Added constructor for testing.
 * *
 * *    Revision 1.8  2006/12/30 09:34:04  animesh
 * *    Added methods for returning nodes with a particular attribute or in a particular region
 * *
 * *    Revision 1.7  2006/12/14 23:19:34  animesh
 * *    NodeInfo used instead of BasicNodeInfo.
 * *
 * *    Revision 1.6  2006/12/13 22:38:29  animesh
 * *    Moved the neighbor table to NetworkTopology.
 * *
 * *    Revision 1.3  2006/12/13 01:29:52  animesh
 * *    Changed from an array of nodeLocatios to an array of BasicNodeInfos.
 * *
 * *    Revision 1.2  2006/10/19 21:21:52  lmottola
 * *    Added CVS preamble.
 * *
 * *
 * *   ATaG (Abstract Task Graph) - Data-driven Macroprogramming for
 * *                                Sensor Networks
 * *
 * *   This program is free software; you can redistribute it and/or
 * *   modify it under the terms of the GNU General Public License
 * *   as published by the Free Software Foundation; either version 2
 * *   of the License, or (at your option) any later version.
 * *
 * *   This program is distributed in the hope that it will be useful,
 * *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 * *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * *   GNU General Public License for more details.
 * *
 * *   You should have received a copy of the GNU General Public License
 * *   along with this program; if not, you may find a copy at the FSF web
 * *   site at 'www.gnu.org' or 'www.fsf.org', or you may write to the
 * *   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * *   Boston, MA  02111-1307, USA
 ***/

package atag.common;

import atag.runtime.NodeInfo;
import atag.runtime.NodeLocation;
import atag.runtime.ln.nodeAttributes.*;
import atag.runtime.ln.predicates.SetMembershipPredicate;
import java.io.*;
import java.util.*;

/**
 * The class that is a representation of the network topology
 * 
 * @author Animesh
 */
public class NetworkTopology {

	private String topologyFile;

	private int m_xRange;

	private int m_yRange;

	private int m_radioRange;

	private int m_sensingRange;

	private ArrayList<NodeInfo> nodes;

	private ArrayList<NodeInfo>[] neighborLists;

	/**
	 * Creates a new instance of NetworkTopology
	 * 
	 * @param topoFileName
	 *            The name of the file with the network topology information
	 */
	public NetworkTopology() {

	}

	public void readTopologyFile(String topoFileName) {

		/*
		 * read the file and add neighbors one by one to the list all info
		 * should be in m_config steal code from networkArchitecture
		 */
		File selectedFile = new File(topoFileName);
		String[] topoArgs;
		StringBuffer contents = new StringBuffer();
		String sContents;

		this.topologyFile = new String(topoFileName);

		BufferedReader input = null;
		try {
			input = new BufferedReader(new FileReader(selectedFile));
			String line = null;
			while ((line = input.readLine()) != null) {
				contents.append(line);
				contents.append(" ");
			}
		} catch (FileNotFoundException ex) {
			ex.printStackTrace();
		} catch (IOException ex) {
			ex.printStackTrace();
		} finally {
			try {
				if (input != null) {
					input.close();
				}
			} catch (IOException ex) {
				ex.printStackTrace();
			}
		}
		sContents = contents.toString();
		topoArgs = sContents.split(" ");

		int i = 0;

		int m_numberOfNodes = Integer.parseInt(topoArgs[i++]);

		// extra functionality -- remove later
		this.nodes = new ArrayList<NodeInfo>(m_numberOfNodes);
		this.neighborLists = new ArrayList[m_numberOfNodes];
		for (int ncount = 0; ncount < m_numberOfNodes; ncount++)
			this.neighborLists[ncount] = new ArrayList<NodeInfo>();
		// //-----------////

		this.m_xRange = Integer.parseInt(topoArgs[i++]);
		this.m_yRange = Integer.parseInt(topoArgs[i++]);
		this.m_radioRange = Integer.parseInt(topoArgs[i++]);
		this.m_sensingRange = Integer.parseInt(topoArgs[i++]);

		while (i < topoArgs.length) {
			int nid = Integer.parseInt(topoArgs[i++]);
			String nPhysicalAddress = topoArgs[i++];
                        int nx = Integer.parseInt(topoArgs[i++]);
			int ny = Integer.parseInt(topoArgs[i++]);
                        
			Vector attributes = new Vector();
            attributes.addElement(new StringAttribute("physicaladdress",nPhysicalAddress));
            /*if (nPhysicalAddress.startsWith("0014.4F01"))
            {
                attributes.add(new StringAttribute("type","freespot"));
            }
            else
            {
                attributes.add(new StringAttribute("type","hostspot"));
            }*/
			// get the region Label
			String[] regionInfo = topoArgs[i++].split(":");
			if (!(regionInfo[0].equalsIgnoreCase("none"))) {
				// add only if there _is_ a region label
				attributes.add(new IntegerAttribute(regionInfo[0], Integer
						.parseInt(regionInfo[1])));
			}

			// get the ability Label
                        String abilityString = topoArgs[i++];
                        if (!(abilityString.equalsIgnoreCase("none"))) {
                                attributes.add(new SetAttribute
                                    (ATaGTaskDeclaration.ABILITY_ATTRIBUTE_NAME, abilityString.split(",")));			
                            }
                                    
            // get the type string
            String type = topoArgs[i++];
            if (type.startsWith("freespot"))
            {
                attributes.add(new StringAttribute("type","freespot"));
            }
            else if (type.startsWith("hostspot"))
            {
                attributes.add(new StringAttribute("type","hostspot"));
            }
            else if (type.startsWith("j2se"))
            {
                attributes.add(new StringAttribute("type","j2se"));
            }
            else if (type.startsWith("android"))
            {
                attributes.add(new StringAttribute("type","android"));
            }

            NodeInfo newNode = new NodeInfo(nid, new NodeLocation(nx, ny),
					attributes);
            newNode.setRegionLabel(regionInfo[0]);
            newNode.setRegionID(Integer.parseInt(regionInfo[1]));
            newNode.setAbilityString(abilityString);
			this.nodes.add(newNode);
		}

		this.discoverNeighbors();
	}

	/**
	 * Creates a new network topology by taking the needed information as input.
	 * This routine is mainly designed for testing purposes.
	 * 
	 * @param nodeList
	 *            an object containing the set of NodeInfo instances
	 * @param radioRange
	 *            the radio range of each node
	 * @param sensingRange
	 *            the sensing range of each node
	 * @param xRange
	 *            the x size of the deployment area
	 * @param yRange
	 *            the y size of the deployment area
	 */
	public NetworkTopology(Set<NodeInfo> nodeList, int radioRange,
			int sensingRange, int xRange, int yRange) {

		this.nodes = new ArrayList<NodeInfo>(nodeList);
		this.m_radioRange = radioRange;
		this.m_sensingRange = sensingRange;
		this.m_xRange = xRange;
		this.m_yRange = yRange;
		this.neighborLists = new ArrayList[nodes.size()];
		for (int i = 0; i < nodes.size(); i++) {
			neighborLists[i] = new ArrayList<NodeInfo>();
		}

		this.discoverNeighbors();
	}

	public int getNumberOfNodes() {
		return this.nodes.size();
	}

	public int getXRange() {
		return this.m_xRange;
	}

	public int getYRange() {
		return this.m_yRange;
	}

	public int getRadioRange() {
		return this.m_radioRange;
	}

	public int getSensingRange() {
		return this.m_sensingRange;
	}

	public String getName() {
		return this.topologyFile;
	}

	public ArrayList<NodeInfo> getNodes() {
		return this.nodes;
	}

	public NodeInfo getNodeInfoByID(int ID) {
		return this.nodes.get(ID);
	}

	private void discoverNeighbors() {
		for (int i = 0; i < this.nodes.size() - 1; i++) {
			for (int j = i + 1; j < this.nodes.size(); j++) {
				// upper triangular matrix is traversed
				if (this.nodes.get(i).getMyLocation().distanceFrom(
						this.nodes.get(j).getMyLocation()) <= this.m_radioRange) {
					// add these nodes to each other's neighbor lists
					this.neighborLists[i].add(this.nodes.get(j));
					this.neighborLists[j].add(this.nodes.get(i));
				}
			}
		}
	}

	public ArrayList<NodeInfo> getNeighborsOf(int nodeID) {
		return this.neighborLists[nodeID];
	}

	public int numberOfNodesWithAttribute(String setName, String memberName) {
		int count = 0;

		SetMembershipPredicate checkPred = new SetMembershipPredicate(
				memberName, SetMembershipPredicate.IS_IN, setName);

		for (NodeInfo n : this.nodes) {
			// for each node, see if it has that attribute
			Vector attributes = (Vector) n.getNodeAttributes();
			Iterator it = attributes.iterator();
			while (it.hasNext()) {
				// for each attribute, see if it matches.
				if (checkPred.matches((NodeAttribute) it.next()))
					count++;

			}
		}
		return count;
	}

	/**
	 * @returns The number of regions the topology is divided into
	 * @param givenRegionName
	 *            The name of the region
	 */
	public int numberOfRegions(String givenRegionName) {
		int regionCount = 0;

		Vector<Integer> regionsList = new Vector<Integer>();

		for (NodeInfo n : this.nodes) {
			// for each node, see if it has that attribute
			Vector attributes = (Vector) n.getNodeAttributes();
			Iterator it = attributes.iterator();
			while (it.hasNext()) {
				NodeAttribute atr = (NodeAttribute) it.next();
				if (atr instanceof IntegerAttribute) {
					// look at the name
					String regionName = atr.getKey();
					if (regionName.equalsIgnoreCase(givenRegionName)) {

						// get regionID
						int rID = ((Integer) atr.getValue()).intValue();

						// iterate over the regionList

						Iterator rIT = regionsList.iterator();
						boolean found = false;
						while (rIT.hasNext()) {
							if (((Integer) rIT.next()).intValue() == rID) {
								found = true;
								break;
							}
						}
						// the loop has ended.
						if (!found) {
							// add this element. Add to count also
							regionsList.add(new Integer(rID));
							regionCount++;
						}// end if
					}// end if

				}// end if
			}// end while
		}// end for

		return regionCount;
	}

	public ArrayList<NodeInfo> getNodesWithAttribute(NodeAttribute na) {

		ArrayList<NodeInfo> retArr = new ArrayList<NodeInfo>();

		for (NodeInfo n : this.nodes) {
			// for each node, see if it has that attribute
			Vector attributes = (Vector) n.getNodeAttributes();
			Iterator it = attributes.iterator();
			while (it.hasNext()) {
				// for each attribute, see if it matches.
				if (((NodeAttribute) it.next()).equals(na)) {
					retArr.add(n);
					break; // jump out of the while. Continue with the next
					// node
				}
			}
		}

		return retArr;
	}
        
        public ArrayList<NodeInfo> getNodesWithAbility(NodeAttribute na) {

		ArrayList<NodeInfo> retArr = new ArrayList<NodeInfo>();

		for (NodeInfo n : this.nodes) {
			// for each node, see if it has the ability
			Vector attributes = (Vector) n.getNodeAttributes();
			Iterator it = attributes.iterator();
			while (it.hasNext()) {
				// for each attribute, see if is the ability attribute
                                NodeAttribute attr= (NodeAttribute) it.next();
                                if(attr.getKey().equals(
                                    ATaGTaskDeclaration.ABILITY_ATTRIBUTE_NAME)) {
                                        HashSet neededValues=(HashSet) na.getValue();                                    
                                        HashSet availableValues=(HashSet) attr.getValue();                                    
                                        if(availableValues.containsAll(neededValues))
                                        {
                                            retArr.add(n);
                                            break;
                                        }
                                }
                        }                    
		}
		return retArr;
	}
        
	public ArrayList<NodeInfo> getNodesInRegion(String givenRegionName,
			int regionID) {

		ArrayList<NodeInfo> retArr = new ArrayList<NodeInfo>();

		for (NodeInfo n : this.nodes) {
			// for each node, see if it has that attribute
			Vector attributes = (Vector) n.getNodeAttributes();
			Iterator it = attributes.iterator();
			while (it.hasNext()) {
				NodeAttribute atr = (NodeAttribute) it.next();
				if (atr instanceof IntegerAttribute) {
					// look at the name
					String regionName = atr.getKey();
					if (regionName.equalsIgnoreCase(givenRegionName)) {

						// get regionID
						int rID = ((Integer) atr.getValue()).intValue();

						if (rID == regionID) {
							retArr.add(n);
							break;// get out of the while, continue with next
							// node
						}

					}// end if

				}// end if
			}// end while
		}// end for

		return retArr;
	}
        
        public ArrayList<Integer> getRegionIDsInRegion(String givenRegionName) {
                
		ArrayList<Integer> retArr = new ArrayList<Integer>();               
		for (NodeInfo n : this.nodes) {
			// for each node, see if it has that attribute
			Vector attributes = (Vector) n.getNodeAttributes();
			Iterator it = attributes.iterator();
			while (it.hasNext()) {
				NodeAttribute atr = (NodeAttribute) it.next();
				if (atr instanceof IntegerAttribute) {
					// look at the name
					String regionName = atr.getKey();
					if (regionName.equalsIgnoreCase(givenRegionName)) {
                                                        if(!retArr.contains(n.getRegionID()))
                                                        {
                                                            retArr.add(n.getRegionID());
                                                            break;// get out of the while, continue with next node
                                                        }
					}// end if
				}// end if
			}// end while
		}// end for
		return retArr;
	}

	public String toString() {

		String ret = "Topology: ";
		Iterator<NodeInfo> it = nodes.iterator();
		while (it.hasNext()) {
			NodeInfo nodeInfo = it.next();
			ret = ret.concat("\n"+nodeInfo.toString());
		}
		return ret;
	}
}
